%{

#include "../include/ast.h"
void yyerror(char *);
int yylex(void);

%}

%union{
	int		number;
	char		strValue[100];
	past		pAst;
}

%token			  IF ELSE INT VOID WHILE RETURN AND OR
%token      <strValue>    ID
%token      <number>      NUMBER
%type       <pAst>	  CompUnit CompUnits Decl VarDecl VarDef InitVal FuncDef Block BlockItem Stmt Exp LVal PrimaryExp Number UnaryExp FuncRParams MulExp AddExp VarDeclMul VarDefMul InitValMul BlockMul

%%

CompUnits: CompUnit                  {$$ = ast::newNextNode((char*)"CompUnit", $1, NULL); node = $$;  }
	| CompUnits CompUnit         {$$ = ast::newNextNode((char*)"CompUnit", $1, $2); node = $$;    }
	;

CompUnit: Decl                      {$$ = $1;}
      | FuncDef                     {$$ = $1;}
      ;

Decl: VarDecl               {$$ = ast::newBasicNode((char*)"Decl", $1, NULL, NULL); }
      ;

VarDeclMul: VarDef                       {$$ = ast::newNextNode((char*)"VarDecl_list", $1, NULL);}
      | VarDeclMul ',' VarDef            {$$ = ast::newNextNode((char*)"VarDecl_list", $1, $3);}
      ;

VarDecl: INT VarDeclMul ';'            {$$ = ast::newBasicNode((char*)"VarDecl", ast::newTypeNode((char*)"int"), $2, NULL);}
      ;

VarDefMul: '[' AddExp ']'              {$$ = ast::newNextNode((char*)"VarDef_list", $2, NULL);}
      | VarDefMul '[' AddExp ']'       {$$ = ast::newNextNode((char*)"VarDef_list", $1, $3);}
      ;

VarDef: ID                               {$$ = ast::newBasicNode((char*)"VarDef", ast::newIDNode(yylval.strValue), NULL,NULL); }
      | ID '=' InitVal                   {$$ = ast::newBasicNode((char*)"VarDef", ast::newIDNode($1), $3, NULL);}
      | ID VarDefMul                     {$$ = ast::newBasicNode((char*)"VarDef",ast::newNextNode((char*)"VarDef_para", ast::newIDNode($1), $2), NULL, NULL);}
      | ID VarDefMul '=' InitVal         {$$ = ast::newBasicNode((char*)"VarDef",ast::newNextNode((char*)"VarDef_para", ast::newIDNode($1), $2), $4, NULL); }
      ;

InitValMul: InitVal                      {$$ = ast::newNextNode((char*)"InitVal_list", $1, NULL);}
      | InitValMul ',' InitVal           {$$ = ast::newNextNode((char*)"InitVal_list", $1, $3);}
      ;

InitVal: Exp                             {$$ = $1;}
      | '{' '}'                          {$$ = ast::newBasicNode((char*)"InitVal_empty", NULL, NULL, NULL);}
      | '{' InitValMul '}'               {$$ = $2;}
      ;

FuncDef: VOID ID '('  ')' Block                   {$$ = ast::newBasicNode((char*)"FuncDef",ast::newNextNode((char*)"FuncDef_para", ast::newTypeNode((char*)"void"), ast::newIDNode($2)), $5, NULL);}
      | INT ID '('  ')' Block                     {$$ = ast::newBasicNode((char*)"FuncDef",ast::newNextNode((char*)"FuncDef_para", ast::newTypeNode((char*)"int"), ast::newIDNode($2)), $5, NULL);}
      ;

BlockMul: BlockItem                 {$$ = ast::newNextNode((char*)"Block_list", $1, NULL);}
      | BlockMul BlockItem          {$$ = ast::newNextNode((char*)"Block_list", $1, $2);}
      ;

Block:  '{' '}'                     {$$ = ast::newBasicNode((char*)"Block_empty", NULL, NULL, NULL);}
      | '{' BlockMul '}'            {$$ = $2;}
      ;

BlockItem: Decl         {$$ = $1;}
      | Stmt            {$$ = $1;}
      ;

Stmt: LVal '=' Exp ';'                          {$$ = ast::newBasicNode((char*)"Assign_Stmt", $1, $3, NULL);}
      | Exp ';'                                 {$$ = $1;}
      | ';'                                     {$$ = ast::newBasicNode((char*)"Stmt_empty", NULL, NULL, NULL);}
      | Block                                   {$$ = $1;}
      | IF '(' AddExp ')' Stmt                  {$$ = ast::newBasicNode((char*)"If_Stmt", $3, $5, NULL);}
      | IF '(' AddExp ')' Stmt ELSE Stmt        {$$ = ast::newBasicNode((char*)"IfElse_Stmt", $3, ast::newNextNode((char*)"If_Else", $5, $7), NULL);}
      | WHILE '(' AddExp ')' Stmt               {$$ = ast::newBasicNode((char*)"While_Stmt", $3, $5, NULL);}
      | RETURN Exp ';'                          {$$ = ast::newBasicNode((char*)"Return_Stmt",$2,NULL,NULL);}
      | RETURN ';'                              {$$ = ast::newBasicNode((char*)"Return_Stmt",NULL,NULL,NULL);}
      ;

Exp: AddExp             {$$ = $1;}
      ;

LVal: ID                {$$ = ast::newIDNode(yylval.strValue);}
      ;

PrimaryExp: '(' Exp ')'       {$$ = $2;}
            | LVal            {$$ = $1;}
            | Number          {$$ = $1;}
            ;

Number: NUMBER                {$$ = ast::newNum(yylval.number);}
      ;

UnaryExp: PrimaryExp                {$$ = $1;}
      | ID '(' ')'                  {$$ = ast::newBasicNode((char*)"UnaryExp", ast::newIDNode($1), NULL, NULL);}
      | ID '(' FuncRParams ')'      {$$ = ast::newBasicNode((char*)"UnaryExp", ast::newIDNode($1), $3, NULL);}
      | '+' UnaryExp                {$$ = ast::newBasicNode((char*)"UnaryExp", ast::newExpr('+', NULL, $2), NULL, NULL);}
      | '-' UnaryExp                {$$ = ast::newBasicNode((char*)"UnaryExp", ast::newExpr('-', NULL, $2), NULL, NULL);}
      ;

FuncRParams: Exp                          {$$ = ast::newNextNode((char*)"FuncRParams_list", $1, NULL);}
            | FuncRParams ',' Exp         {$$ = ast::newNextNode((char*)"FuncRParams_list", $1, $3);}
            ;

MulExp: UnaryExp                          {$$ = $1;}
      | MulExp '*' UnaryExp               {$$ = ast::newExpr('*', $1, $3);}
      | MulExp '/' UnaryExp               {$$ = ast::newExpr('/', $1, $3);}
      ;

AddExp: MulExp                           {$$ = $1;}
      | AddExp '+' MulExp                {$$ = ast::newExpr('+', $1, $3);}
      | AddExp '-' MulExp                {$$ = ast::newExpr('-', $1, $3);}
      ;

